package com.xdb.sqlparser.dialect.sql99.parser;

import java.util.List;

import com.xdb.sqlparser.ast.SQLExpr;
import com.xdb.sqlparser.ast.SQLOrderBy;
import com.xdb.sqlparser.ast.SQLSetQuantifier;
import com.xdb.sqlparser.ast.expr.SQLIdentifierExpr;
import com.xdb.sqlparser.ast.statement.SQLExprTableSource;
import com.xdb.sqlparser.ast.statement.SQLJoinTableSource;
import com.xdb.sqlparser.ast.statement.SQLSelect;
import com.xdb.sqlparser.ast.statement.SQLSelectGroupByClause;
import com.xdb.sqlparser.ast.statement.SQLSelectItem;
import com.xdb.sqlparser.ast.statement.SQLSelectQuery;
import com.xdb.sqlparser.ast.statement.SQLSelectQueryBlock;
import com.xdb.sqlparser.ast.statement.SQLSubqueryTableSource;
import com.xdb.sqlparser.ast.statement.SQLTableSource;
import com.xdb.sqlparser.ast.statement.SQLUnionOperator;
import com.xdb.sqlparser.ast.statement.SQLUnionQuery;
import com.xdb.sqlparser.ast.statement.SQLUnionQueryTableSource;
import com.xdb.sqlparser.ast.statement.SQLWithSubqueryClause;
import com.xdb.sqlparser.exception.ParserException;
import com.xdb.sqlparser.parser.SQLLexer;
import com.xdb.sqlparser.parser.SQLParser;
import com.xdb.sqlparser.parser.SQLToken;

public class SQL99SelectParser extends SQLParser {

	protected SQL99ExprParser exprParser;

	public SQL99SelectParser(String sql) {
		super(sql);
	}

	public SQL99SelectParser(SQLLexer lexer) {
		super(lexer);
	}

	public SQL99SelectParser(SQL99ExprParser exprParser) {
		super(exprParser.getLexer());
		this.exprParser = exprParser;
	}

	public SQLSelect select() {
		SQLSelect select = new SQLSelect();

		withSubquery(select);

		select.setQuery(query());
		select.setOrderBy(parseOrderBy());

		if (select.getOrderBy() == null) {
			select.setOrderBy(parseOrderBy());
		}

		while (lexer.token() == SQLToken.HINT) {
			this.exprParser.parseHints(select.getHints());
		}

		return select;
	}

	protected SQLUnionQuery createSQLUnionQuery() {
		return new SQLUnionQuery();
	}

	public SQLUnionQuery unionRest(SQLUnionQuery union) {
		if (lexer.token() == SQLToken.ORDER) {
			SQLOrderBy orderBy = this.exprParser.parseOrderBy();
			union.setOrderBy(orderBy);
			return unionRest(union);
		}
		return union;
	}
	/**
	 * 查询重置<br>
	 * 如果当前标识符是UNION,则创建Union查询对象，并将输入的查询语句设置为UNION对象的LEFT属性中；如果当前标识符含有ALL，
	 * 则将操作设置为UNION ALL
	 * 
	 * @param selectQuery
	 * @return
	 */
	public SQLSelectQuery queryRest(SQLSelectQuery selectQuery) {
		if (lexer.token() == SQLToken.UNION) {
			lexer.nextToken();

			SQLUnionQuery union = createSQLUnionQuery();
			union.setLeft(selectQuery);

			if (lexer.token() == SQLToken.ALL) {
				union.setOperator(SQLUnionOperator.UNION_ALL);
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.DISTINCT) {
				union.setOperator(SQLUnionOperator.DISTINCT);
				lexer.nextToken();
			}
			SQLSelectQuery right = this.query();
			union.setRight(right);

			return unionRest(union);
		}

		if (lexer.token() == SQLToken.EXCEPT) {
			lexer.nextToken();

			SQLUnionQuery union = new SQLUnionQuery();
			union.setLeft(selectQuery);

			union.setOperator(SQLUnionOperator.EXCEPT);

			SQLSelectQuery right = this.query();
			union.setRight(right);

			return union;
		}

		if (lexer.token() == SQLToken.INTERSECT) {
			lexer.nextToken();

			SQLUnionQuery union = new SQLUnionQuery();
			union.setLeft(selectQuery);

			union.setOperator(SQLUnionOperator.INTERSECT);

			SQLSelectQuery right = this.query();
			union.setRight(right);

			return union;
		}

		if (lexer.token() == SQLToken.MINUS) {
			lexer.nextToken();

			SQLUnionQuery union = new SQLUnionQuery();
			union.setLeft(selectQuery);

			union.setOperator(SQLUnionOperator.MINUS);

			SQLSelectQuery right = this.query();
			union.setRight(right);

			return union;
		}

		return selectQuery;
	}

	public SQLSelectQuery query() {
		if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();

			SQLSelectQuery select = query();
			accept(SQLToken.RPAREN);

			return queryRest(select);
		}

		accept(SQLToken.SELECT);

		if (lexer.token() == SQLToken.COMMENT) {
			lexer.nextToken();
		}

		SQLSelectQueryBlock queryBlock = new SQLSelectQueryBlock();

		if (lexer.token() == SQLToken.DISTINCT) {
			queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT);
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.UNIQUE) {
			queryBlock.setDistionOption(SQLSetQuantifier.UNIQUE);
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.ALL) {
			queryBlock.setDistionOption(SQLSetQuantifier.ALL);
			lexer.nextToken();
		}
		// 解析SELECT后声明的列
		parseSelectList(queryBlock);
		// 解析FROM关键字
		parseFrom(queryBlock);
		// 解析WHERE关键字
		parseWhere(queryBlock);
		// 解析GROUP　BY关键字
		parseGroupBy(queryBlock);

		return queryRest(queryBlock);
	}

	protected void withSubquery(SQLSelect select) {
		if (lexer.token() == SQLToken.WITH) {
			lexer.nextToken();

			SQLWithSubqueryClause withQueryClause = new SQLWithSubqueryClause();

			if (lexer.token() == SQLToken.RECURSIVE || identifierEquals("RECURSIVE")) {
				lexer.nextToken();
				withQueryClause.setRecursive(true);
			}

			for (;;) {
				SQLWithSubqueryClause.Entry entry = new SQLWithSubqueryClause.Entry();
				entry.setParent(withQueryClause);
				entry.setName((SQLIdentifierExpr) this.exprParser.name());

				if (lexer.token() == SQLToken.LPAREN) {
					lexer.nextToken();
					exprParser.names(entry.getColumns());
					accept(SQLToken.RPAREN);
				}

				accept(SQLToken.AS);
				accept(SQLToken.LPAREN);
				entry.setSubQuery(select());
				accept(SQLToken.RPAREN);

				withQueryClause.getEntries().add(entry);

				if (lexer.token() == SQLToken.COMMA) {
					lexer.nextToken();
					continue;
				}

				break;
			}

			select.setWithSubQuery(withQueryClause);
		}
	}

	protected void parseWhere(SQLSelectQueryBlock queryBlock) {
		if (lexer.token() == SQLToken.WHERE) {
			lexer.nextToken();

			queryBlock.setWhere(expr());
		}
	}
	/**
	 * 解析查询语句块中的分组部分
	 * 
	 * @param queryBlock
	 */
	protected void parseGroupBy(SQLSelectQueryBlock queryBlock) {
		// 如果标识为GROUP关键字，读取下一个关键字BY
		if (lexer.token() == SQLToken.GROUP) {
			lexer.nextToken();
			// 检查下是否接受BY
			accept(SQLToken.BY);
			// 创建一个Group By对应的抽象对象
			SQLSelectGroupByClause groupBy = new SQLSelectGroupByClause();
			while (true) {
				groupBy.addItem(expr());
				// 如果当前符号不是逗号，则结束扫描
				if (lexer.token() != SQLToken.COMMA) {
					break;
				}
				lexer.nextToken();
			}
			// 如果当前符号是HAVING，则进行解析，把HAVING子句中的条件解析出来
			if (lexer.token() == SQLToken.HAVING) {
				lexer.nextToken();
				groupBy.setHaving(expr());
			}
			// 将Group By对应的抽象对象设置会查询子句
			queryBlock.setGroupBy(groupBy);
			// 如果含有HAVING子句，则解析
		} else if (lexer.token() == (SQLToken.HAVING)) {
			lexer.nextToken();
			SQLSelectGroupByClause groupBy = new SQLSelectGroupByClause();
			groupBy.setHaving(this.expr());
			// 将Group By对应的抽象对象设置会查询子句
			queryBlock.setGroupBy(groupBy);
		}
	}

	protected void parseSelectList(SQLSelectQueryBlock queryBlock) {
		final List<SQLSelectItem> selectList = queryBlock.getSelectList();
		for (;;) {
			final SQLSelectItem selectItem = this.exprParser.parseSelectItem();
			selectList.add(selectItem);
			selectItem.setParent(queryBlock);

			if (lexer.token() != SQLToken.COMMA) {
				break;
			}

			lexer.nextToken();
		}
	}

	public void parseFrom(SQLSelectQueryBlock queryBlock) {
		if (lexer.token() != SQLToken.FROM) {
			return;
		}

		lexer.nextToken();

		queryBlock.setFrom(parseTableSource());
	}

	public SQLTableSource parseTableSource() {
		if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();
			SQLTableSource tableSource;
			if (lexer.token() == SQLToken.SELECT || lexer.token() == SQLToken.WITH) {
				SQLSelect select = select();
				accept(SQLToken.RPAREN);
				SQLSelectQuery query = queryRest(select.getQuery());
				if (query instanceof SQLUnionQuery) {
					tableSource = new SQLUnionQueryTableSource((SQLUnionQuery) query);
				} else {
					tableSource = new SQLSubqueryTableSource(select);
				}
			} else if (lexer.token() == SQLToken.LPAREN) {
				tableSource = parseTableSource();
				accept(SQLToken.RPAREN);
			} else {
				tableSource = parseTableSource();
				accept(SQLToken.RPAREN);
			}

			return parseTableSourceRest(tableSource);
		}

		if (lexer.token() == SQLToken.SELECT) {
			throw new ParserException("TODO");
		}

		SQLExprTableSource tableReference = new SQLExprTableSource();

		parseTableSourceQueryTableExpr(tableReference);

		return parseTableSourceRest(tableReference);
	}

	private void parseTableSourceQueryTableExpr(SQLExprTableSource tableReference) {
		if (lexer.token() == SQLToken.LITERAL_ALIAS || lexer.token() == SQLToken.IDENTIFIED || lexer.token() == SQLToken.LITERAL_CHARS) {
			tableReference.setExpr(this.exprParser.name());
			return;
		}

		tableReference.setExpr(expr());
	}

	protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) {
		if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) {
			if (lexer.token() != SQLToken.LEFT && lexer.token() != SQLToken.RIGHT && lexer.token() != SQLToken.FULL && !identifierEquals("STRAIGHT_JOIN") && !identifierEquals("CROSS")
					&& lexer.token() != SQLToken.OUTER) {
				String alias = as();
				if (alias != null) {
					tableSource.setAlias(alias);
					return parseTableSourceRest(tableSource);
				}
			}
		}

		SQLJoinTableSource.JoinType joinType = null;

		if (lexer.token() == SQLToken.LEFT) {
			lexer.nextToken();
			if (lexer.token() == SQLToken.OUTER) {
				lexer.nextToken();
			}

			accept(SQLToken.JOIN);
			joinType = SQLJoinTableSource.JoinType.LEFT_OUTER_JOIN;
		} else if (lexer.token() == SQLToken.RIGHT) {
			lexer.nextToken();
			if (lexer.token() == SQLToken.OUTER) {
				lexer.nextToken();
			}
			accept(SQLToken.JOIN);
			joinType = SQLJoinTableSource.JoinType.RIGHT_OUTER_JOIN;
		} else if (lexer.token() == SQLToken.FULL) {
			lexer.nextToken();
			if (lexer.token() == SQLToken.OUTER) {
				lexer.nextToken();
			}
			accept(SQLToken.JOIN);
			joinType = SQLJoinTableSource.JoinType.FULL_OUTER_JOIN;
		} else if (lexer.token() == SQLToken.INNER) {
			lexer.nextToken();
			accept(SQLToken.JOIN);
			joinType = SQLJoinTableSource.JoinType.INNER_JOIN;
		} else if (lexer.token() == SQLToken.JOIN) {
			lexer.nextToken();
			joinType = SQLJoinTableSource.JoinType.JOIN;
		} else if (lexer.token() == SQLToken.COMMA) {
			lexer.nextToken();
			joinType = SQLJoinTableSource.JoinType.COMMA;
		} else if (identifierEquals("STRAIGHT_JOIN")) {
			lexer.nextToken();
			joinType = SQLJoinTableSource.JoinType.STRAIGHT_JOIN;
		} else if (identifierEquals("CROSS")) {
			lexer.nextToken();
			if (lexer.token() == SQLToken.JOIN) {
				lexer.nextToken();
				joinType = SQLJoinTableSource.JoinType.CROSS_JOIN;
			} else if (identifierEquals("APPLY")) {
				lexer.nextToken();
				joinType = SQLJoinTableSource.JoinType.CROSS_APPLY;
			}
		} else if (lexer.token() == SQLToken.OUTER) {
			lexer.nextToken();
			if (identifierEquals("APPLY")) {
				lexer.nextToken();
				joinType = SQLJoinTableSource.JoinType.OUTER_APPLY;
			}
		}

		if (joinType != null) {
			SQLJoinTableSource join = new SQLJoinTableSource();
			join.setLeft(tableSource);
			join.setJoinType(joinType);
			join.setRight(parseTableSource());

			if (lexer.token() == SQLToken.ON) {
				lexer.nextToken();
				join.setCondition(expr());
			} else if (identifierEquals("USING")) {
				lexer.nextToken();
				if (lexer.token() == SQLToken.LPAREN) {
					lexer.nextToken();
					this.exprParser.exprList(join.getUsing(), join);
					accept(SQLToken.RPAREN);
				} else {
					join.getUsing().add(this.expr());
				}
			}

			return parseTableSourceRest(join);
		}

		return tableSource;
	}
	/**
	 * 获取表达式
	 * 
	 * @return
	 */
	public SQLExpr expr() {
		return this.exprParser.expr();
	}

	public SQLOrderBy parseOrderBy() {
		return this.exprParser.parseOrderBy();
	}

	public void acceptKeyword(String ident) {
		if (lexer.token() == SQLToken.IDENTIFIER && ident.equalsIgnoreCase(lexer.stringVal())) {
			lexer.nextToken();
		} else {
			setErrorEndPos(lexer.pos());
			throw new ParserException("syntax error, expect " + ident + ", actual " + lexer.token());
		}
	}
}
